package com.snail.cloud.config;

import com.snail.cloud.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration  //标记此类为一个springboot的配置类
@EnableWebSecurity  //开启security基于web开发的安全机制
@EnableMethodSecurity
public class SecurityConfig {
    @Autowired
    private UserService userService;


    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPointImpl;

    @Autowired
    private AccessDeniedHandlerImpl accessDeniedHandlerImpl;

    @Autowired
    private JwtTokenFilter jwtTokenFilter;
    /*
    密码加密器，
    	用户表中的用户密码等敏感信息都需要加密存储
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    //2.配置认证管理器，security框架默认不提供
    @Bean
    public AuthenticationManager authenticationManager() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        //设置securityUserDetailService，告知security框架，按照指定的类进行身份校验
        daoAuthenticationProvider.setUserDetailsService(userService);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        MobilePhoneVerificationCodeProvider mobilePhoneVerificationCodeProvider = new MobilePhoneVerificationCodeProvider();
        mobilePhoneVerificationCodeProvider.setUserDetailsService(userService);
        ProviderManager pm = new ProviderManager(daoAuthenticationProvider, mobilePhoneVerificationCodeProvider);
        return pm;
    }

    //3.配置springsecurity的放行路径等信息
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return web -> {
            web.ignoring().requestMatchers("/user/login"
                    , "/user/register"
                    , "/user/code/login"
                    , "/error"
                    ,"/v2/api-docs"
                    , "/configuration/ui"
                    , "/swagger-resources"
                    , "/configuration/security"
                    , "/swagger-ui.html"
                    , "/webjars/**"
                    , "/user/password/forget"

            );
        };
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(new MobilePhoneVerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
//                        .dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll()
                        .requestMatchers("/snail/login", "/snail/code/login", "/hello").permitAll()
                        .requestMatchers(HttpMethod.OPTIONS).permitAll()
                        .anyRequest().authenticated()
                );

        //关闭 防止客户端的 csrf（跨站伪造） 攻击行为 的能力
        // 从security过滤器链中撤出 CsrfFilter
        http.csrf(AbstractHttpConfigurer::disable);
        //将自定义的token认证过滤器加入到security-filterChian中，并指定其位置
        http.exceptionHandling(cuse ->{
            cuse.authenticationEntryPoint(authenticationEntryPointImpl)
                    .accessDeniedHandler(accessDeniedHandlerImpl);
                }
        );
        return http.build();
    }


}
